/*
 * Copyright (C) 2011, Pavel Samko <bulldozerbsg@gmail.com>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the "Pavel Samko" nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

#include "VCModWaveOutFile.h"

#define VC_WAVE_FORMAT_PCM 1

#pragma pack (push, 1)
typedef struct VC_WAVEFORMATEX
{
    unsigned short		wFormatTag;
    unsigned short		nChannels;
    unsigned long		nSamplesPerSec;
    unsigned long		nAvgBytesPerSec;
    unsigned short		nBlockAlign;
    unsigned short		wBitsPerSample;
    unsigned short		cbSize;
} VC_WAVEFORMATEX;

typedef struct VC_WAVEHEADER
{
	char				szRIFF[4];
	unsigned long		lRIFFSize;
	char				szWave[4];
	char				szFmt[4];
	unsigned long		lFmtSize;
	VC_WAVEFORMATEX		wfex;
	char				szData[4];
	unsigned long		lDataSize;
} VC_WAVEHEADER;
#pragma pack (pop)

VCModWaveOutFile::VCModWaveOutFile(VCIController & controller, std::string & filename)
: VCIMod(controller)
, m_filename(filename)
, m_file(NULL)
, m_state(WORK_IDLE)
, m_streamsize(0)
{
	controller.OnNotifyFromMod(*this, VC_INFO_INIT);
}

VCModWaveOutFile::~VCModWaveOutFile()
{
	
	if (m_file)
	{
		if (m_state == WORK_STREAM)
		{
			fseek(m_file, 4, SEEK_SET);
			unsigned long size = m_streamsize + sizeof(VC_WAVEHEADER) - 8;
			fwrite(&size, 4, 1, m_file);
			fseek(m_file, 42, SEEK_SET);
			fwrite(&m_streamsize, 4, 1, m_file);
		}
		fflush(m_file);
		fclose(m_file);
		m_file = NULL;
	}
}

VC_RESULT VCModWaveOutFile::Push(VC_CMD cmd, void * data_buff, size_t data_size, VC_DATAFORMAT data_format)
{
	VC_RESULT result = VC_FALSE;
	if (data_format == VC_PCM_1_32000_S16)
	{
		switch(cmd)
		{
		case VC_STREAM_BEGIN:
			if ((m_state == WORK_IDLE) && !m_file)
			{
				m_streamsize = 0;
				m_file = fopen(m_filename.c_str(), "wb");
				if (m_file)
				{
					VC_WAVEHEADER head;
					sprintf(head.szRIFF, "RIFF");
					head.lRIFFSize = 0;
					sprintf(head.szWave, "WAVE");
					sprintf(head.szFmt, "fmt ");
					head.lFmtSize = sizeof(VC_WAVEFORMATEX);
					head.wfex.nChannels = 1;
					head.wfex.wBitsPerSample = 16;
					head.wfex.wFormatTag = VC_WAVE_FORMAT_PCM;
					head.wfex.nSamplesPerSec = 32000;
					head.wfex.nBlockAlign = head.wfex.nChannels * head.wfex.wBitsPerSample / 8;
					head.wfex.nAvgBytesPerSec = head.wfex.nSamplesPerSec * head.wfex.nBlockAlign;
					head.wfex.cbSize = 0;
					sprintf(head.szData, "data");
					head.lDataSize = 0;

					fwrite(&head, sizeof(VC_WAVEHEADER), 1, m_file);

					if (data_buff && data_size)
					{
						fwrite(data_buff, data_size, 1, m_file);
						m_streamsize += data_size;
					}
					m_controller.OnNotifyFromMod(*this, VC_INFO_STREAM_BEGIN);
					m_state = WORK_STREAM;
					result = VC_OK;
				} else
				{
					m_controller.OnNotifyFromMod(*this, VC_ERROR);
				}
			}
			break;
		case VC_STREAM:
			if ((m_state == WORK_STREAM) && m_file && data_buff && data_size)
			{
				fwrite(data_buff, data_size, 1, m_file);
				m_streamsize += data_size;
				result = VC_OK;
			}
			break;
		case VC_STREAM_END:
			if ((m_state == WORK_STREAM))
			{
				if (m_file)
				{
					if (data_buff && data_size)
					{
						fwrite(data_buff, data_size, 1, m_file);
						m_streamsize += data_size;
					}

					fseek(m_file, 4, SEEK_SET);
					unsigned long size = m_streamsize + sizeof(VC_WAVEHEADER) - 8;
					fwrite(&size, 4, 1, m_file);
					fseek(m_file, 42, SEEK_SET);
					fwrite(&m_streamsize, 4, 1, m_file);

					fflush(m_file);
					fclose(m_file);
					m_file = NULL;
				}
				m_controller.OnNotifyFromMod(*this, VC_INFO_STREAM_END);
				m_state = WORK_IDLE;
				result = VC_OK;
			}
			break;
		default:
			break;
		}
	}
	return result;
}

